/*-------------------------------------------------------------------------
 *
 * ts_parse.c
 *		tsearch的主要解析函数
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 *
 *
 * IDENTIFICATION
 *	  src/backend/tsearch/ts_parse.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "tsearch/ts_cache.h"
#include "tsearch/ts_utils.h"

#define IGNORE_LONGLEXEME	1

/*
 * Lexize子系统
 */

typedef struct ParsedLex
{
	int			type;
	char	   *lemm;
	int			lenlemm;
	struct ParsedLex *next;
} ParsedLex;

typedef struct ListParsedLex
{
	ParsedLex  *head;
	ParsedLex  *tail;
} ListParsedLex;

typedef struct
{
	TSConfigCacheEntry *cfg;
	Oid			curDictId;
	int			posDict;
	DictSubState dictState;
	ParsedLex  *curSub;
	ListParsedLex towork;		/* 当前工作列表 */
	ListParsedLex waste;		/* 已经词汇化的词素列表 */

	/*
	 * 用于存储最后一个要词汇化的变体的字段（基本上是词库或类似于需要几个词素的内容）
	 */

	ParsedLex  *lastRes;
	TSLexeme   *tmpRes;
} LexizeData;

static void fc_LexizeInit(LexizeData *fc_ld, TSConfigCacheEntry *fc_cfg)
{
	fc_ld->cfg = fc_cfg;
	fc_ld->curDictId = InvalidOid;
	fc_ld->posDict = 0;
	fc_ld->towork.head = fc_ld->towork.tail = fc_ld->curSub = NULL;
	fc_ld->waste.head = fc_ld->waste.tail = NULL;
	fc_ld->lastRes = NULL;
	fc_ld->tmpRes = NULL;
}

static void fc_LPLAddTail(ListParsedLex *fc_list, ParsedLex *fc_newpl)
{
	if (fc_list->tail)
	{
		fc_list->tail->next = fc_newpl;
		fc_list->tail = fc_newpl;
	}
	else
		fc_list->head = fc_list->tail = fc_newpl;
	fc_newpl->next = NULL;
}

static ParsedLex *
fc_LPLRemoveHead(ListParsedLex *fc_list)
{
	ParsedLex  *fc_res = fc_list->head;

	if (fc_list->head)
		fc_list->head = fc_list->head->next;

	if (fc_list->head == NULL)
		fc_list->tail = NULL;

	return fc_res;
}

static void fc_LexizeAddLemm(LexizeData *fc_ld, int fc_type, char *fc_lemm, int fc_lenlemm)
{
	ParsedLex  *fc_newpl = (ParsedLex *) palloc(sizeof(ParsedLex));

	fc_newpl->type = fc_type;
	fc_newpl->lemm = fc_lemm;
	fc_newpl->lenlemm = fc_lenlemm;
	fc_LPLAddTail(&fc_ld->towork, fc_newpl);
	fc_ld->curSub = fc_ld->towork.tail;
}

static void fc_RemoveHead(LexizeData *fc_ld)
{
	fc_LPLAddTail(&fc_ld->waste, fc_LPLRemoveHead(&fc_ld->towork));

	fc_ld->posDict = 0;
}

static void fc_setCorrLex(LexizeData *fc_ld, ParsedLex **fc_correspondLexem)
{
	if (fc_correspondLexem)
	{
		*fc_correspondLexem = fc_ld->waste.head;
	}
	else
	{
		ParsedLex  *fc_tmp,
				   *fc_ptr = fc_ld->waste.head;

		while (fc_ptr)
		{
			fc_tmp = fc_ptr->next;
			pfree(fc_ptr);
			fc_ptr = fc_tmp;
		}
	}
	fc_ld->waste.head = fc_ld->waste.tail = NULL;
}

static void fc_moveToWaste(LexizeData *fc_ld, ParsedLex *fc_stop)
{
	bool		fc_go = true;

	while (fc_ld->towork.head && fc_go)
	{
		if (fc_ld->towork.head == fc_stop)
		{
			fc_ld->curSub = fc_stop->next;
			fc_go = false;
		}
		fc_RemoveHead(fc_ld);
	}
}

static void fc_setNewTmpRes(LexizeData *fc_ld, ParsedLex *fc_lex, TSLexeme *fc_res)
{
	if (fc_ld->tmpRes)
	{
		TSLexeme   *fc_ptr;

		for (fc_ptr = fc_ld->tmpRes; fc_ptr->lexeme; fc_ptr++)
			pfree(fc_ptr->lexeme);
		pfree(fc_ld->tmpRes);
	}
	fc_ld->tmpRes = fc_res;
	fc_ld->lastRes = fc_lex;
}

static TSLexeme * fc_LexizeExec(LexizeData *fc_ld, ParsedLex **fc_correspondLexem)
{
	int			fc_i;
	ListDictionary *fc_map;
	TSDictionaryCacheEntry *fc_dict;
	TSLexeme   *fc_res;

	if (fc_ld->curDictId == InvalidOid)
	{
		/*
		 * 通常模式：字典只想要一个词，但我们应该牢记我们应该遍历所有堆栈
		 */

		while (fc_ld->towork.head)
		{
			ParsedLex  *fc_curVal = fc_ld->towork.head;
			char	   *fc_curValLemm = fc_curVal->lemm;
			int			fc_curValLenLemm = fc_curVal->lenlemm;

			fc_map = fc_ld->cfg->map + fc_curVal->type;

			if (fc_curVal->type == 0 || fc_curVal->type >= fc_ld->cfg->lenmap || fc_map->len == 0)
			{
				/* 跳过这种类型的词素 */
				fc_RemoveHead(fc_ld);
				continue;
			}

			for (fc_i = fc_ld->posDict; fc_i < fc_map->len; fc_i++)
			{
				fc_dict = lookup_ts_dictionary_cache(fc_map->dictIds[fc_i]);

				fc_ld->dictState.isend = fc_ld->dictState.getnext = false;
				fc_ld->dictState.private_state = NULL;
				fc_res = (TSLexeme *) DatumGetPointer(FunctionCall4(&(fc_dict->lexize),
																 PointerGetDatum(fc_dict->dictData),
																 PointerGetDatum(fc_curValLemm),
																 Int32GetDatum(fc_curValLenLemm),
																 PointerGetDatum(&fc_ld->dictState)));

				if (fc_ld->dictState.getnext)
				{
					/*
					 * 字典想要下一个词，因此设置并存储当前位置并进入多词模式
					 */

					fc_ld->curDictId = DatumGetObjectId(fc_map->dictIds[fc_i]);
					fc_ld->posDict = fc_i + 1;
					fc_ld->curSub = fc_curVal->next;
					if (fc_res)
						fc_setNewTmpRes(fc_ld, fc_curVal, fc_res);
					return fc_LexizeExec(fc_ld, fc_correspondLexem);
				}

				if (!fc_res)		/* 字典不知道这个词素 */
					continue;

				if (fc_res->flags & TSL_FILTER)
				{
					fc_curValLemm = fc_res->lexeme;
					fc_curValLenLemm = strlen(fc_res->lexeme);
					continue;
				}

				fc_RemoveHead(fc_ld);
				fc_setCorrLex(fc_ld, fc_correspondLexem);
				return fc_res;
			}

			fc_RemoveHead(fc_ld);
		}
	}
	else
	{							/* curDictId是有效的 */
		fc_dict = lookup_ts_dictionary_cache(fc_ld->curDictId);

		/*
		 * 字典ld->curDictId询问我们关于后续词汇
		 */

		while (fc_ld->curSub)
		{
			ParsedLex  *fc_curVal = fc_ld->curSub;

			fc_map = fc_ld->cfg->map + fc_curVal->type;

			if (fc_curVal->type != 0)
			{
				bool		fc_dictExists = false;

				if (fc_curVal->type >= fc_ld->cfg->lenmap || fc_map->len == 0)
				{
					/* 跳过这种类型的词素 */
					fc_ld->curSub = fc_curVal->next;
					continue;
				}

				/*
				 * 我们必须确保当前类型的词素被我们的字典识别：我们仅检查它是否存在于字典列表中？
				 */
				for (fc_i = 0; fc_i < fc_map->len && !fc_dictExists; fc_i++)
					if (fc_ld->curDictId == DatumGetObjectId(fc_map->dictIds[fc_i]))
						fc_dictExists = true;

				if (!fc_dictExists)
				{
					/*
					 * 字典无法处理当前类型的词素，返回基本模式并重新执行所有存储的词素
					 */
					fc_ld->curDictId = InvalidOid;
					return fc_LexizeExec(fc_ld, fc_correspondLexem);
				}
			}

			fc_ld->dictState.isend = (fc_curVal->type == 0);
			fc_ld->dictState.getnext = false;

			fc_res = (TSLexeme *) DatumGetPointer(FunctionCall4(&(fc_dict->lexize),
															 PointerGetDatum(fc_dict->dictData),
															 PointerGetDatum(fc_curVal->lemm),
															 Int32GetDatum(fc_curVal->lenlemm),
															 PointerGetDatum(&fc_ld->dictState)));

			if (fc_ld->dictState.getnext)
			{
				/* 字典想要再一个 */
				fc_ld->curSub = fc_curVal->next;
				if (fc_res)
					fc_setNewTmpRes(fc_ld, fc_curVal, fc_res);
				continue;
			}

			if (fc_res || fc_ld->tmpRes)
			{
				/*
				 * 字典规范化词素，因此我们从堆栈中移除所有已使用的词素，返回基本模式并重新执行堆栈末尾（如果存在）
				 */
				if (fc_res)
				{
					fc_moveToWaste(fc_ld, fc_ld->curSub);
				}
				else
				{
					fc_res = fc_ld->tmpRes;
					fc_moveToWaste(fc_ld, fc_ld->lastRes);
				}

				/* 重置为初始状态 */
				fc_ld->curDictId = InvalidOid;
				fc_ld->posDict = 0;
				fc_ld->lastRes = NULL;
				fc_ld->tmpRes = NULL;
				fc_setCorrLex(fc_ld, fc_correspondLexem);
				return fc_res;
			}

			/*
			 * 字典不想要下一个词素并且没有识别出任何内容，从ld->towork.head重新执行
			 */
			fc_ld->curDictId = InvalidOid;
			return fc_LexizeExec(fc_ld, fc_correspondLexem);
		}
	}

	fc_setCorrLex(fc_ld, fc_correspondLexem);
	return NULL;
}

/*
 * 解析字符串并词汇化单词。
 *
 * prs将被填充。
 */
void parsetext(Oid fc_cfgId, ParsedText *fc_prs, char *fc_buf, int fc_buflen)
{
	int			fc_type,
				fc_lenlemm;
	char	   *fc_lemm = NULL;
	LexizeData	fc_ldata;
	TSLexeme   *fc_norms;
	TSConfigCacheEntry *fc_cfg;
	TSParserCacheEntry *fc_prsobj;
	void	   *fc_prsdata;

	fc_cfg = lookup_ts_config_cache(fc_cfgId);
	fc_prsobj = lookup_ts_parser_cache(fc_cfg->prsId);

	fc_prsdata = (void *) DatumGetPointer(FunctionCall2(&fc_prsobj->prsstart,
													 PointerGetDatum(fc_buf),
													 Int32GetDatum(fc_buflen)));

	fc_LexizeInit(&fc_ldata, fc_cfg);

	do
	{
		fc_type = DatumGetInt32(FunctionCall3(&(fc_prsobj->prstoken),
										   PointerGetDatum(fc_prsdata),
										   PointerGetDatum(&fc_lemm),
										   PointerGetDatum(&fc_lenlemm)));

		if (fc_type > 0 && fc_lenlemm >= MAXSTRLEN)
		{
#ifdef IGNORE_LONGLEXEME
			ereport(NOTICE,
					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
					 errmsg("word is too long to be indexed"),
					 errdetail("Words longer than %d characters are ignored.",
							   MAXSTRLEN)));
			continue;
#else
			ereport(ERROR,
					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
					 errmsg("word is too long to be indexed"),
					 errdetail("Words longer than %d characters are ignored.",
							   MAXSTRLEN)));
#endif
		}

		fc_LexizeAddLemm(&fc_ldata, fc_type, fc_lemm, fc_lenlemm);

		while ((fc_norms = fc_LexizeExec(&fc_ldata, NULL)) != NULL)
		{
			TSLexeme   *fc_ptr = fc_norms;

			fc_prs->pos++;			/* 设置位置 */

			while (fc_ptr->lexeme)
			{
				if (fc_prs->curwords == fc_prs->lenwords)
				{
					fc_prs->lenwords *= 2;
					fc_prs->words = (ParsedWord *) repalloc((void *) fc_prs->words, fc_prs->lenwords * sizeof(ParsedWord));
				}

				if (fc_ptr->flags & TSL_ADDPOS)
					fc_prs->pos++;
				fc_prs->words[fc_prs->curwords].len = strlen(fc_ptr->lexeme);
				fc_prs->words[fc_prs->curwords].word = fc_ptr->lexeme;
				fc_prs->words[fc_prs->curwords].nvariant = fc_ptr->nvariant;
				fc_prs->words[fc_prs->curwords].flags = fc_ptr->flags & TSL_PREFIX;
				fc_prs->words[fc_prs->curwords].alen = 0;
				fc_prs->words[fc_prs->curwords].pos.pos = LIMITPOS(fc_prs->pos);
				fc_ptr++;
				fc_prs->curwords++;
			}
			pfree(fc_norms);
		}
	} while (fc_type > 0);

	FunctionCall1(&(fc_prsobj->prsend), PointerGetDatum(fc_prsdata));
}

/*
 * 标题框架
 */

/* 将单词添加到prs->words[] */
static void fc_hladdword(HeadlineParsedText *fc_prs, char *fc_buf, int fc_buflen, int fc_type)
{
	if (fc_prs->curwords >= fc_prs->lenwords)
	{
		fc_prs->lenwords *= 2;
		fc_prs->words = (HeadlineWordEntry *) repalloc((void *) fc_prs->words, fc_prs->lenwords * sizeof(HeadlineWordEntry));
	}
	memset(&(fc_prs->words[fc_prs->curwords]), 0, sizeof(HeadlineWordEntry));
	fc_prs->words[fc_prs->curwords].type = (uint8) fc_type;
	fc_prs->words[fc_prs->curwords].len = fc_buflen;
	fc_prs->words[fc_prs->curwords].word = palloc(fc_buflen);
	memcpy(fc_prs->words[fc_prs->curwords].word, fc_buf, fc_buflen);
	fc_prs->curwords++;
}

/*
 * 将pos和匹配查询项数据添加到刚添加的单词。
 * 此处，buf/buflen表示处理过的词素，而不是原始标记文本。
 *
 * 如果查询包含多个匹配项，我们复制最后添加的单词，以便可以指向每个项。
 * 复制的条目标记为repeated = 1。
 */
static void fc_hlfinditem(HeadlineParsedText *fc_prs, TSQuery fc_query, int32 fc_pos, char *fc_buf, int fc_buflen)
{
	int			fc_i;
	QueryItem  *fc_item = GETQUERY(fc_query);
	HeadlineWordEntry *fc_word;

	while (fc_prs->curwords + fc_query->size >= fc_prs->lenwords)
	{
		fc_prs->lenwords *= 2;
		fc_prs->words = (HeadlineWordEntry *) repalloc((void *) fc_prs->words, fc_prs->lenwords * sizeof(HeadlineWordEntry));
	}

	fc_word = &(fc_prs->words[fc_prs->curwords - 1]);
	fc_word->pos = LIMITPOS(fc_pos);
	for (fc_i = 0; fc_i < fc_query->size; fc_i++)
	{
		if (fc_item->type == QI_VAL &&
			tsCompareString(GETOPERAND(fc_query) + fc_item->qoperand.distance, fc_item->qoperand.length,
							fc_buf, fc_buflen, fc_item->qoperand.prefix) == 0)
		{
			if (fc_word->item)
			{
				memcpy(&(fc_prs->words[fc_prs->curwords]), fc_word, sizeof(HeadlineWordEntry));
				fc_prs->words[fc_prs->curwords].item = &fc_item->qoperand;
				fc_prs->words[fc_prs->curwords].repeated = 1;
				fc_prs->curwords++;
			}
			else
				fc_word->item = &fc_item->qoperand;
		}
		fc_item++;
	}
}

static void fc_addHLParsedLex(HeadlineParsedText *fc_prs, TSQuery fc_query, ParsedLex *fc_lexs, TSLexeme *fc_norms)
{
	ParsedLex  *fc_tmplexs;
	TSLexeme   *fc_ptr;
	int32		fc_savedpos;

	while (fc_lexs)
	{
		if (fc_lexs->type > 0)
			fc_hladdword(fc_prs, fc_lexs->lemm, fc_lexs->lenlemm, fc_lexs->type);

		fc_ptr = fc_norms;
		fc_savedpos = fc_prs->vectorpos;
		while (fc_ptr && fc_ptr->lexeme)
		{
			if (fc_ptr->flags & TSL_ADDPOS)
				fc_savedpos++;
			fc_hlfinditem(fc_prs, fc_query, fc_savedpos, fc_ptr->lexeme, strlen(fc_ptr->lexeme));
			fc_ptr++;
		}

		fc_tmplexs = fc_lexs->next;
		pfree(fc_lexs);
		fc_lexs = fc_tmplexs;
	}

	if (fc_norms)
	{
		fc_ptr = fc_norms;
		while (fc_ptr->lexeme)
		{
			if (fc_ptr->flags & TSL_ADDPOS)
				fc_prs->vectorpos++;
			pfree(fc_ptr->lexeme);
			fc_ptr++;
		}
		pfree(fc_norms);
	}
}

void hlparsetext(Oid fc_cfgId, HeadlineParsedText *fc_prs, TSQuery fc_query, char *fc_buf, int fc_buflen)
{
	int			fc_type,
				fc_lenlemm;
	char	   *fc_lemm = NULL;
	LexizeData	fc_ldata;
	TSLexeme   *fc_norms;
	ParsedLex  *fc_lexs;
	TSConfigCacheEntry *fc_cfg;
	TSParserCacheEntry *fc_prsobj;
	void	   *fc_prsdata;

	fc_cfg = lookup_ts_config_cache(fc_cfgId);
	fc_prsobj = lookup_ts_parser_cache(fc_cfg->prsId);

	fc_prsdata = (void *) DatumGetPointer(FunctionCall2(&(fc_prsobj->prsstart),
													 PointerGetDatum(fc_buf),
													 Int32GetDatum(fc_buflen)));

	fc_LexizeInit(&fc_ldata, fc_cfg);

	do
	{
		fc_type = DatumGetInt32(FunctionCall3(&(fc_prsobj->prstoken),
										   PointerGetDatum(fc_prsdata),
										   PointerGetDatum(&fc_lemm),
										   PointerGetDatum(&fc_lenlemm)));

		if (fc_type > 0 && fc_lenlemm >= MAXSTRLEN)
		{
#ifdef IGNORE_LONGLEXEME
			ereport(NOTICE,
					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
					 errmsg("word is too long to be indexed"),
					 errdetail("Words longer than %d characters are ignored.",
							   MAXSTRLEN)));
			continue;
#else
			ereport(ERROR,
					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
					 errmsg("word is too long to be indexed"),
					 errdetail("Words longer than %d characters are ignored.",
							   MAXSTRLEN)));
#endif
		}

		fc_LexizeAddLemm(&fc_ldata, fc_type, fc_lemm, fc_lenlemm);

		do
		{
			if ((fc_norms = fc_LexizeExec(&fc_ldata, &fc_lexs)) != NULL)
			{
				fc_prs->vectorpos++;
				fc_addHLParsedLex(fc_prs, fc_query, fc_lexs, fc_norms);
			}
			else
				fc_addHLParsedLex(fc_prs, fc_query, fc_lexs, NULL);
		} while (fc_norms);
	} while (fc_type > 0);

	FunctionCall1(&(fc_prsobj->prsend), PointerGetDatum(fc_prsdata));
}

/*
 * 从HeadlineParsedText生成文本对象的标题。
 */
text * generateHeadline(HeadlineParsedText *fc_prs)
{
	text	   *fc_out;
	char	   *fc_ptr;
	int			fc_len = 128;
	int			fc_numfragments = 0;
	int16		fc_infrag = 0;

	HeadlineWordEntry *fc_wrd = fc_prs->words;

	fc_out = (text *) palloc(fc_len);
	fc_ptr = ((char *) fc_out) + VARHDRSZ;

	while (fc_wrd - fc_prs->words < fc_prs->curwords)
	{
		while (fc_wrd->len + fc_prs->stopsellen + fc_prs->startsellen + fc_prs->fragdelimlen + (fc_ptr - ((char *) fc_out)) >= fc_len)
		{
			int			fc_dist = fc_ptr - ((char *) fc_out);

			fc_len *= 2;
			fc_out = (text *) repalloc(fc_out, fc_len);
			fc_ptr = ((char *) fc_out) + fc_dist;
		}

		if (fc_wrd->in && !fc_wrd->repeated)
		{
			if (!fc_infrag)
			{

				/* 新片段的开始 */
				fc_infrag = 1;
				fc_numfragments++;
				/* 如果这是在第一个片段之后，则添加片段分隔符 */
				if (fc_numfragments > 1)
				{
					memcpy(fc_ptr, fc_prs->fragdelim, fc_prs->fragdelimlen);
					fc_ptr += fc_prs->fragdelimlen;
				}
			}
			if (fc_wrd->replace)
			{
				*fc_ptr = ' ';
				fc_ptr++;
			}
			else if (!fc_wrd->skip)
			{
				if (fc_wrd->selected)
				{
					memcpy(fc_ptr, fc_prs->startsel, fc_prs->startsellen);
					fc_ptr += fc_prs->startsellen;
				}
				memcpy(fc_ptr, fc_wrd->word, fc_wrd->len);
				fc_ptr += fc_wrd->len;
				if (fc_wrd->selected)
				{
					memcpy(fc_ptr, fc_prs->stopsel, fc_prs->stopsellen);
					fc_ptr += fc_prs->stopsellen;
				}
			}
		}
		else if (!fc_wrd->repeated)
		{
			if (fc_infrag)
				fc_infrag = 0;
			pfree(fc_wrd->word);
		}

		fc_wrd++;
	}

	SET_VARSIZE(fc_out, fc_ptr - ((char *) fc_out));
	return fc_out;
}
